package com.jade.bss.security.login;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;

import com.jade.bss.admin.account.AdminAccount;
import com.jade.bss.admin.account.AdminAccountManager;
import com.jade.bss.admin.grant.GrantManager;
import com.jade.bss.admin.journal.AdminJournal;
import com.jade.bss.admin.permission.PermissionEntry;
import com.jade.bss.admin.principal.AdminPrincipal;
import com.jade.bss.admin.principal.PermissionPrincipal;
import com.jade.bss.admin.principal.SimplePrincipal;
import com.jade.bss.admin.role.RoleManager;
import com.jade.bss.base.BssConstants;
import com.jade.framework.base.context.ApplicationContextUtils;
import com.jade.framework.base.security.auth.login.AbstractLoginModule;
import com.jade.framework.base.util.Parameters;
import com.jade.journal.Journal;
import com.jade.journal.JournalUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Created with IntelliJ IDEA.
 * User: jinya
 * Date: 2017/6/24
 * Time: 14:40
 * TEL :17701640710   MAIL:2009_jinya@163.com
 * To change this template use File | Settings | File Templates.
 */
public class AdminLoginModule extends AbstractLoginModule
{
    protected Log logger = LogFactory.getLog(AdminLoginModule.class.getName());
    protected boolean succeeded = false;
    protected boolean commitSucceeded = false;
    protected List<String> permissionList;
    protected AdminPrincipal adminPrincipal;
    protected AdminAccountManager adminAccountManager = ApplicationContextUtils.getBean(
            "bss_adminAccountManager");
    protected RoleManager roleManager = ApplicationContextUtils.getBean("bss_roleManager");
    protected GrantManager grantManager = ApplicationContextUtils.getBean("bss_grantManager");
    protected Journal journal = JournalUtils.getJournal(AdminJournal.NAME);

    protected boolean doLogin(Subject subject, Parameters parameters)
            throws LoginException
    {
        String username = parameters.getString(LoginConstants.PARAM_USERNAME);
        String password = parameters.getString(LoginConstants.PARAM_PASSWORD);
        String type = parameters.getString(LoginConstants.PARAM_TYPE, AdminAccount.DEFAULT_TYPE);
        String ownerType = parameters.getString(LoginConstants.PARAM_OWNER_TYPE, AdminAccount.DEFAULT_OWNER_TYPE);
        String ownerId = parameters.getString(LoginConstants.PARAM_OWNER_ID, AdminAccount.DEFAULT_OWNER_ID);
        AdminAccount adminAccount = null;
        try {
            adminAccount = adminAccountManager.validateAdmin(type, ownerType, ownerId, username, password);
            if (adminAccount.getStatus() == 0) {
                adminPrincipal = createPrincipal(adminAccount, parameters);
                try {
                    permissionList = getPermissions(adminAccount);
                }
                catch (Exception e) {
                    logger.warn("It is failed to get permissioms for  " + username + ":", e);
                    throw new LoginException("err.bss.admin.security.login.unexpected_error");
                }
                succeeded = true;
                return true;
            }
            else {
                logger.warn("The status is invalid for  for  " + username);
                throw new LoginException("err.bss.admin.security.login.invalid_admin");
            }
        }
        catch (LoginException ex) {
            throw ex;
        }
        catch (Exception ex) {
            logger.warn("It is failed to authenticate  " + username + ":", ex);
            throw new LoginException("err.bss.admin.security.login.auth_error");
        }
    }

    protected AdminPrincipal createPrincipal(AdminAccount account, Parameters parameters)
    {
        String clientAddress = parameters.getString(LoginConstants.PARAM_CLIENT_ADDRESS);
        String clientType = parameters.getString(LoginConstants.PARAM_CLIENT_TYPE);
        adminPrincipal = new AdminPrincipal();
        adminPrincipal.setId(account.getId());
        adminPrincipal.setLastLoginTime(account.getLastLoginTime());
        adminPrincipal.setType(account.getType());
        adminPrincipal.setOwnerId(account.getOwnerId());
        adminPrincipal.setOwnerType(account.getOwnerType());
        adminPrincipal.setName(account.getName());
        adminPrincipal.setTrueName(account.getRealName());
        adminPrincipal.setClientAddress(clientAddress);
        adminPrincipal.setClientType(clientType);
        adminPrincipal.setUserInfo(account);
        return adminPrincipal;
    }

    protected List<String> getPermissions(AdminAccount account)
            throws Exception
    {
        List<String> permissions = new ArrayList<String>();
        SimplePrincipal principal = new SimplePrincipal();
        principal.setName(String.valueOf(account.getId()));
        principal.setType(SimplePrincipal.TYPE_ADMIN);
        List<PermissionEntry> perList = grantManager.getPermissions(principal);
        if (perList != null) {
            for (PermissionEntry perEntry : perList) {
                permissions.add(perEntry.getName());
            }
        }
        return permissions;
    }

    @Override
    protected void doCommit(Subject subject, Parameters parameters)
            throws LoginException
    {
        if (succeeded) {
            if (logger.isDebugEnabled()) {
                logger.debug("login commit for " + adminPrincipal.getName());
            }
            subject.getPrincipals().add(adminPrincipal);
            if (permissionList != null) {
                HashSet<String> h = new HashSet(permissionList);
                permissionList.clear();
                permissionList.addAll(h);
                for (String perName : permissionList) {
                    subject.getPrincipals().add(new PermissionPrincipal(perName));
                }
            }
            updateAdmin(adminPrincipal.getId());
            createLog("login", adminPrincipal);
            permissionList = null;
            commitSucceeded = true;
        }
        else {
            logger.warn("login commit failed!");
        }
    }

    @Override
    protected void doLogout(Subject subject, Parameters parameters)
            throws LoginException
    {
        if (adminPrincipal != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("logout for " + adminPrincipal.getName());
            }
            createLog("logout", adminPrincipal);
            subject.getPrincipals().remove(adminPrincipal);
        }
        else {
            logger.debug("logout for unknown principal");
        }
        succeeded = false;
        commitSucceeded = false;
        permissionList = null;
    }

    public boolean abort()
            throws LoginException
    {
        if (logger.isDebugEnabled()) {
            logger.debug("login abort: succeeded= " + succeeded + " commitSucceeded=" + commitSucceeded);
        }

        if (succeeded) {
            if (!commitSucceeded) {
                permissionList = null;
            }
            else {
                logout();
            }
            return true;
        }
        return false;
    }

    protected void createLog(String action, AdminPrincipal user)
    {
        AdminJournal aj = new AdminJournal();
        aj.setOwnerType(user.getOwnerType());
        aj.setOwnerId(user.getOwnerId());
        aj.setAdminName(user.getName());
        aj.setOperatorType(BssConstants.OPERATOR_TYPE_ADMIN);
        aj.setOperator(String.valueOf(user.getId()));
        aj.setAction(action);
        aj.setModule("sso");
        aj.setResult(true);
        aj.setOpDate(new Date());
        aj.setIp(user.getClientAddress());
        //aj.setClientType(user.getClientType());
        aj.setOwnerType(user.getOwnerType());
        aj.setOwnerId(user.getOwnerId());
        journal.infoEntity(aj);
    }

    protected void updateAdmin(long userId)
    {
        AdminAccount adminAccount = adminAccountManager.getAdmin((int) userId);
        if (adminAccount != null) {
            adminAccount.setLastLoginTime(new Date());
            try {
                adminAccountManager.updateAdmin(adminAccount);
            }
            catch (Exception e) {
                logger.warn("Can't update admin last login info", e);
            }
        }
    }
}
